#ifndef CBC_H_INCLUDED
#define CBC_H_INCLUDED

using namespace std;
#include <string>
#include <fstream>
#include <stdlib.h>
#include "Utils.h"
#include "TriDES.h"

class CBC{
public:
    static void encryptFile(string sourceFile, string encryptedFile, string key){
        ifstream inFile(sourceFile.c_str(),ios::binary);
        ofstream outFile(encryptedFile.c_str(),ios::binary);
        if(!inFile){
            cout<<"error open the file for encrypting!";
            exit(0);
        }
        inFile.seekg(0, inFile.end);
        int length = inFile.tellg();
        inFile.seekg(0, inFile.beg);
        cout<<"the size of file is "<<length<<endl;
        bool res = false;
        int over = length % 8;
        char * buffer;
        if(over != 0){
            cout<<"the size is not the 8n"<<endl;
            length += (8 - over);
            buffer = new char[length];
            inFile.read(buffer, length);
            res = true;
            for(int i = 8 - over; i > 0; i--){
                buffer[length - i] = over;
            }
        }else{
            buffer = new char[length];
            inFile.read(buffer, length);
        }
        //the encrypt module:
        char *tempBuf = new char[8];
        //PreTask
        for(int j = 0; j < 8; j++){
            tempBuf[j] = buffer[j];
        }
        string IV = "0000000000000000000000000000000000000000000000000000000000000000";
        string plaintext = Utils().binaryBlock2String(8, tempBuf);
        string ciphertext = TriDES().encrypt(Utils().XOR(IV, plaintext), key);
        Utils().string2binaryBlock(ciphertext, tempBuf);
        outFile.write(tempBuf, 8);
        //for:
        for(int i = 8; i < length; i+=8){
            for(int j = 0; j < 8; j++){
                tempBuf[j] = buffer[i + j];
            }
            plaintext = Utils().binaryBlock2String(8, tempBuf);
            ciphertext = TriDES().encrypt(Utils().XOR(ciphertext, plaintext), key);
            Utils().string2binaryBlock(ciphertext, tempBuf);
            outFile.write(tempBuf, 8);
        }
        delete tempBuf;
        tempBuf = new char[1];
        tempBuf[0] = res ? 1 : 0;
        outFile.write(tempBuf,1);
        delete tempBuf;
        delete buffer;
        inFile.close();
        outFile.close();
    }
    static void decryptFile(string sourceFile, string decryptedFile, string key){
        ifstream inFile(sourceFile.c_str(),ios::binary);
        ofstream outFile(decryptedFile.c_str(),ios::binary);
        if(!inFile){
            cout<<"error open the file for decrypting!";
            exit(0);
        }
        inFile.seekg(0, inFile.end);
        int length = inFile.tellg();
        inFile.seekg(0, inFile.beg);
        cout<<"the size of file is "<<length<<endl;
        char *buffer = new char[length];
        inFile.read(buffer, length);
        char *tempBuf = new char[8];
        int isFilled = buffer[--length];
        if(isFilled){//the file is be filled
            int end = length - 8;
            for(int j = 0; j < 8; j++){
                tempBuf[j] = buffer[j];
            }
            string ciphertext = Utils().binaryBlock2String(8, tempBuf);
            string plaintext = TriDES().decrypt(ciphertext, key);
            string pre = "";
            string IV = "0000000000000000000000000000000000000000000000000000000000000000";
            plaintext = Utils().XOR(plaintext, IV);
            Utils().string2binaryBlock(plaintext, tempBuf);
            outFile.write(tempBuf, 8);
            for(int i = 8; i < end; i+=8){
                for(int j = 0; j < 8; j++){
                    tempBuf[j] = buffer[i + j];
                }
                pre = ciphertext;
                ciphertext = Utils().binaryBlock2String(8, tempBuf);
                plaintext = TriDES().decrypt(ciphertext, key);
                plaintext = Utils().XOR(plaintext, pre);
                Utils().string2binaryBlock(plaintext, tempBuf);
                outFile.write(tempBuf, 8);
            }
            for(int i = 0; i < 8; i++){
                tempBuf[i] = buffer[end + i];
            }
            pre = ciphertext;
            ciphertext = Utils().binaryBlock2String(8, tempBuf);
            plaintext = TriDES().decrypt(ciphertext, key);
            plaintext = Utils().XOR(plaintext, pre);
            Utils().string2binaryBlock(plaintext, tempBuf);
            int over = tempBuf[7];
            outFile.write(tempBuf, over);
        }else{//the file is not be filled
            for(int j = 0; j < 8; j++){
                tempBuf[j] = buffer[j];
            }
            string ciphertext = Utils().binaryBlock2String(8, tempBuf);
            string plaintext = TriDES().decrypt(ciphertext, key);
            string pre = "";
            string IV = "0000000000000000000000000000000000000000000000000000000000000000";
            plaintext = Utils().XOR(plaintext, IV);
            Utils().string2binaryBlock(plaintext, tempBuf);
            outFile.write(tempBuf, 8);
            for(int i = 8; i < length; i+=8){
                for(int j = 0; j < 8; j++){
                    tempBuf[j] = buffer[i + j];
                }
                pre = ciphertext;
                ciphertext = Utils().binaryBlock2String(8, tempBuf);
                plaintext = TriDES().decrypt(ciphertext, key);
                plaintext = Utils().XOR(plaintext, pre);
                Utils().string2binaryBlock(plaintext, tempBuf);
                outFile.write(tempBuf, 8);
            }
        }
        delete tempBuf;
        delete buffer;
        inFile.close();
        outFile.close();
    }
};

#endif // CBC_H_INCLUDED

